﻿/*
 *    Distrefine deliverable version 1.
 *    Satisfies "Deliverable Features for version 1, Issue 4:
 *    http://code.google.com/p/distrefine/issues/detail?id=4
 *
 *    Suitable for a testcase.
 *    Some tidbits gleaned from Eric Paris's example from:
 *    http://git.infradead.org/users/eparis/fanotify-example.git
 *
 *
 *    Copyright (C) 2011  Jeff Wicks
 *
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/



#define _ATFILE_SOURCE
#define _GNU_SOURCE

#include <errno.h>
#include <inttypes.h>
#include <fcntl.h>
#include <linux/limits.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <linux/fanotify.h>


int main(int argc, char *argv[])
{
    int fan_fd;
    uint64_t fan_mask = FAN_OPEN | FAN_CLOSE | FAN_ACCESS | FAN_MODIFY;
    unsigned int mark_flags = FAN_MARK_ADD | FAN_MARK_MOUNT;
    ssize_t len;
    char buf[4096];

    //change to a type.
    typedef fd_set fdset_t;
    fdset_t readonly_file_descriptor;

    struct fanotify_event_metadata *metadata;


    if ((fan_fd = syscall(__NR_fanotify_init, 0, O_RDONLY | O_LARGEFILE)) < 0)
    {
        perror("__NR_fanotify_init failed:");
        return 1;
    }

    /*
        If mount point was given on the command line, mark it for watching.
        Don't care to ensure this is a valid mount point right.
    */
    if (argv[1] == NULL)
    {
        puts("Provide a mount point to watch!");
        return 1;
    }
    else if (syscall(__NR_fanotify_mark, fan_fd, mark_flags, fan_mask, AT_FDCWD, argv[1]) < 0)
    {
        perror("mark_object failed!");
        return 1;
    }


    FD_ZERO(&readonly_file_descriptor);
    FD_SET(fan_fd, &readonly_file_descriptor);

    while (1)
    {
        if (select(fan_fd+1, &readonly_file_descriptor, NULL, NULL, NULL) < 0)
        {
            perror("select failed: ");
            return 1;
        }

        len = read(fan_fd, buf, sizeof(buf));

        metadata = (void *)buf;
        while(FAN_EVENT_OK(metadata, len))
        {
            if (metadata->fd >= 0)
            {
                char path[PATH_MAX];
                int path_len;

                sprintf(path, "/proc/self/fd/%d", metadata->fd);
                path_len = readlink(path, path, sizeof(path)-1);
                if (path_len < 0)
                {
                    puts("!! path_len < 0");
                    return 1;
                }
                path[path_len] = '\0';
                printf("%s:", path);
            }
            else
                printf("?:");

            printf(" pid=%ld", (long)metadata->pid);

            if (metadata->mask & FAN_OPEN)
                printf(" open");

            printf("\n");

            if (metadata->fd >= 0 && close(metadata->fd) != 0)
            {
                puts("!! metadata->fd >= 0 && close(metadata->fd) != 0  !!");
                return 1;
            }

            metadata = FAN_EVENT_NEXT(metadata, len);
        }

        if (len < 0)
        {
            puts("!!len < 0.");
            return 1;
        }
    }

    return 0;
}


